package com.chatmcp.mcprouter.service.impl;

import com.chatmcp.mcprouter.service.CacheService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.RedisConnectionFailureException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.util.Arrays;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

/**
 * Redis缓存服务增强实现类
 * 支持单节点和集群模式，提供更完善的缓存策略和机制
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class RedisCacheService implements CacheService {

    private final RedisTemplate<String, Object> redisTemplate;
    private final ObjectMapper objectMapper;
    private final RedisConnectionFactory connectionFactory;
    
    // 本地缓存备份，用于Redis不可用时的故障转移
    private final Map<String, CacheEntry> localCache = new ConcurrentHashMap<>();
    
    // 缓存配置参数
    @Value("${app.cache.ttl:86400}")
    private long defaultTtl;
    
    @Value("${app.cache.enable:true}")
    private boolean cacheEnabled;
    
    @Value("${spring.redis.cluster.enabled:false}")
    private boolean isCluster;
    
    // 缓存命中次数和失效次数统计
    private long hitCount = 0;
    private long missCount = 0;
    private long errorCount = 0;
    
    // 缓存键前缀
    private static final String GLOBAL_KEY_PREFIX = "mcpr:";
    
    /**
     * 缓存条目类，存储值和过期时间
     */
    private static class CacheEntry {
        private final Object value;
        private final long expireTime;
        
        public CacheEntry(Object value, long ttlSeconds) {
            this.value = value;
            this.expireTime = System.currentTimeMillis() + (ttlSeconds * 1000);
        }
        
        public boolean isExpired() {
            return System.currentTimeMillis() > expireTime;
        }
        
        public Object getValue() {
            return value;
        }
    }
    
    /**
     * 初始化方法
     */
    @PostConstruct
    public void init() {
        if (!cacheEnabled) {
            log.info("Cache is disabled by configuration");
            return;
        }
        
        try {
            StringBuilder infoBuilder = new StringBuilder();
            infoBuilder.append("Redis cache initialized. ");
            
            if (isCluster) {
                infoBuilder.append("Mode: Cluster. ");
            } else {
                infoBuilder.append("Mode: Standalone. ");
            }
            
            // 测试连接
            RedisConnection connection = connectionFactory.getConnection();
            connection.ping();
            connection.close();
            
            infoBuilder.append("Connection verified successfully.");
            log.info(infoBuilder.toString());
        } catch (Exception e) {
            log.error("Failed to initialize Redis cache: {}", e.getMessage());
            log.warn("Falling back to local cache only");
        }
        
        // 启动定期清理任务
        startLocalCacheCleanupTask();
    }
    
    /**
     * 启动定期清理本地缓存中过期条目的任务
     */
    private void startLocalCacheCleanupTask() {
        Thread cleanupThread = new Thread(() -> {
            try {
                while (true) {
                    try {
                        // 每分钟清理一次过期的本地缓存
                        Thread.sleep(60 * 1000);
                        cleanupExpiredEntries();
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                        break;
                    } catch (Exception e) {
                        log.error("Error in local cache cleanup task: {}", e.getMessage());
                    }
                }
            } finally {
                log.info("Local cache cleanup task stopped");
            }
        });
        cleanupThread.setDaemon(true);
        cleanupThread.setName("LocalCacheCleanup");
        cleanupThread.start();
        log.debug("Started local cache cleanup task");
    }
    
    /**
     * 清理过期的本地缓存条目
     */
    private void cleanupExpiredEntries() {
        int removedCount = 0;
        for (Map.Entry<String, CacheEntry> entry : localCache.entrySet()) {
            if (entry.getValue().isExpired()) {
                localCache.remove(entry.getKey());
                removedCount++;
            }
        }
        if (removedCount > 0) {
            log.debug("Removed {} expired entries from local cache", removedCount);
        }
    }
    
    @Override
    public boolean initCache(String cacheName) {
        if (!cacheEnabled) {
            log.info("Cache is disabled, skipping initialization of {}", cacheName);
            return false;
        }
        
        if (!"redis".equals(cacheName)) {
            log.warn("Unsupported cache type: {}", cacheName);
            return false;
        }
        
        try {
            // 测试Redis连接
            RedisConnection connection = connectionFactory.getConnection();
            connection.ping();
            connection.close();
            
            log.info("Redis cache {} initialized successfully", cacheName);
            return true;
        } catch (Exception e) {
            log.error("Redis cache {} initialization failed: {}", cacheName, e.getMessage());
            return false;
        }
    }

    @Override
    public boolean hasKey(String key) {
        if (!cacheEnabled) return false;
        
        String fullKey = buildKey(key);
        
        // 先检查本地缓存
        CacheEntry localEntry = localCache.get(fullKey);
        if (localEntry != null) {
            if (localEntry.isExpired()) {
                localCache.remove(fullKey);
                return false;
            }
            return true;
        }
        
        // 再检查Redis
        try {
            Boolean result = redisTemplate.hasKey(fullKey);
            return Boolean.TRUE.equals(result);
        } catch (Exception e) {
            log.warn("Error checking Redis key {}: {}", fullKey, e.getMessage());
            errorCount++;
            return false;
        }
    }

    @Override
    @SuppressWarnings("unchecked")
    public <T> T get(String key, Class<T> clazz) {
        if (!cacheEnabled) return null;
        
        String fullKey = buildKey(key);
        
        // 先尝试从本地缓存获取
        CacheEntry localEntry = localCache.get(fullKey);
        if (localEntry != null) {
            if (localEntry.isExpired()) {
                localCache.remove(fullKey);
            } else {
                Object cachedValue = localEntry.getValue();
                if (cachedValue != null && clazz.isAssignableFrom(cachedValue.getClass())) {
                    hitCount++;
                    log.debug("Cache hit (local) for key: {}", key);
                    return (T) cachedValue;
                }
            }
        }
        
        // 尝试从Redis获取
        try {
            Object value = redisTemplate.opsForValue().get(fullKey);
            if (value == null) {
                missCount++;
                log.debug("Cache miss for key: {}", key);
                return null;
            }
            
            T result;
            try {
                if (value instanceof String) {
                    if (clazz == String.class) {
                        result = (T) value;
                    } else {
                        result = objectMapper.readValue((String) value, clazz);
                    }
                } else if (clazz.isAssignableFrom(value.getClass())) {
                    result = (T) value;
                } else {
                    // 数据类型不匹配，尝试通过JSON转换
                    String json = objectMapper.writeValueAsString(value);
                    result = objectMapper.readValue(json, clazz);
                }
                
                // 更新本地缓存
                // 使用Redis中的剩余过期时间或默认过期时间
                Long ttl = redisTemplate.getExpire(fullKey, TimeUnit.SECONDS);
                long cacheTime = (ttl != null && ttl > 0) ? ttl : defaultTtl;
                localCache.put(fullKey, new CacheEntry(result, cacheTime));
                
                hitCount++;
                log.debug("Cache hit (redis) for key: {}", key);
                return result;
            } catch (Exception e) {
                log.error("Failed to deserialize cache value for key: {}", key, e);
                missCount++;
                return null;
            }
        } catch (Exception e) {
            log.warn("Error getting value from Redis for key {}: {}", key, e.getMessage());
            errorCount++;
            return null;
        }
    }

    @Override
    public <T> void set(String key, T value) {
        set(key, value, defaultTtl, TimeUnit.SECONDS);
    }

    @Override
    public <T> void set(String key, T value, long timeout, TimeUnit unit) {
        if (!cacheEnabled || value == null) return;
        
        String fullKey = buildKey(key);
        
        // 计算超时时间（秒）
        long timeoutSeconds = unit.toSeconds(timeout);
        
        // 更新本地缓存
        localCache.put(fullKey, new CacheEntry(value, timeoutSeconds));
        
        // 更新Redis缓存
        try {
            redisTemplate.opsForValue().set(fullKey, value, timeout, unit);
            log.debug("Stored in cache: key={}, ttl={}s", key, timeoutSeconds);
        } catch (Exception e) {
            log.warn("Failed to store value in Redis for key {}: {}", key, e.getMessage());
            errorCount++;
            // 已经存入本地缓存，不需要额外处理
        }
    }

    @Override
    public boolean delete(String key) {
        if (!cacheEnabled) return false;
        
        String fullKey = buildKey(key);
        
        // 从本地缓存删除
        localCache.remove(fullKey);
        
        // 从Redis删除
        try {
            Boolean result = redisTemplate.delete(fullKey);
            log.debug("Deleted from cache: key={}, result={}", key, result);
            return Boolean.TRUE.equals(result);
        } catch (Exception e) {
            log.warn("Failed to delete key {} from Redis: {}", key, e.getMessage());
            errorCount++;
            return false;
        }
    }
    
    /**
     * 删除匹配模式的所有键
     * 
     * @param pattern 键模式
     * @return 删除的键数量
     */
    public long deleteByPattern(String pattern) {
        if (!cacheEnabled) return 0;
        
        String fullPattern = buildKey(pattern);
        
        // 从本地缓存删除匹配的键
        long localRemoved = 0;
        for (String key : localCache.keySet()) {
            if (key.matches(fullPattern.replace("*", ".*"))) {
                localCache.remove(key);
                localRemoved++;
            }
        }
        
        // 从Redis删除
        try {
            Set<String> keys = redisTemplate.keys(fullPattern);
            if (keys != null && !keys.isEmpty()) {
                Long count = redisTemplate.delete(keys);
                log.debug("Deleted {} keys matching pattern: {}", count, pattern);
                return count != null ? count : 0;
            }
            return 0;
        } catch (Exception e) {
            log.warn("Failed to delete keys matching pattern {} from Redis: {}", pattern, e.getMessage());
            errorCount++;
            return localRemoved; // 返回本地删除的数量
        }
    }
    
    /**
     * 获取当前缓存统计信息
     * 
     * @return 缓存统计信息
     */
    public Map<String, Object> getStats() {
        Map<String, Object> stats = new ConcurrentHashMap<>();
        stats.put("enabled", cacheEnabled);
        stats.put("mode", isCluster ? "cluster" : "standalone");
        stats.put("hitCount", hitCount);
        stats.put("missCount", missCount);
        stats.put("errorCount", errorCount);
        stats.put("localCacheSize", localCache.size());
        
        try {
            // 尝试获取Redis状态信息
            RedisConnection connection = connectionFactory.getConnection();
            stats.put("redisConnected", true);
            connection.close();
        } catch (Exception e) {
            stats.put("redisConnected", false);
            stats.put("redisError", e.getMessage());
        }
        
        return stats;
    }
    
    /**
     * 重置缓存统计计数器
     */
    public void resetStats() {
        hitCount = 0;
        missCount = 0;
        errorCount = 0;
    }
    
    /**
     * 构建完整的缓存键名
     * 
     * @param key 原始键名
     * @return 添加前缀后的完整键名
     */
    private String buildKey(String key) {
        if (key.startsWith(GLOBAL_KEY_PREFIX)) {
            return key;
        }
        return GLOBAL_KEY_PREFIX + key;
    }
    
    /**
     * 带重试的Redis操作执行器
     * 
     * @param <T> 返回类型
     * @param operation Redis操作
     * @param key 缓存键（用于日志）
     * @param defaultValue 失败时的默认返回值
     * @return 操作结果或默认值
     */
    private <T> T executeWithRetry(Supplier<T> operation, String key, T defaultValue) {
        int maxRetries = 2;
        int retryCount = 0;
        
        while (retryCount <= maxRetries) {
            try {
                return operation.get();
            } catch (RedisConnectionFailureException e) {
                retryCount++;
                if (retryCount <= maxRetries) {
                    log.warn("Redis connection failure for key {}, retry {}/{}", key, retryCount, maxRetries);
                    try {
                        Thread.sleep(100 * retryCount); // 指数退避
                    } catch (InterruptedException ie) {
                        Thread.currentThread().interrupt();
                        return defaultValue;
                    }
                } else {
                    log.error("Failed to execute Redis operation for key {} after {} retries", key, maxRetries);
                    errorCount++;
                    return defaultValue;
                }
            } catch (Exception e) {
                log.error("Error executing Redis operation for key {}: {}", key, e.getMessage());
                errorCount++;
                return defaultValue;
            }
        }
        
        return defaultValue;
    }
} 